{-
Nota: Parser adaptado de "Write Yourself a Scheme in 48 Hours". Modificações em readExpr e LispVal para ajustar funcionalidade. Modificação para ele retornar o código parseado junto com o nó. Também criei uma função para transformar tudo numa árvore de Tokens. Esse parser é incompleto mas razoavelmente funcional.
-}
--module SchemeParser (parseScheme) where
module SchemeParser where

import Text.ParserCombinators.Parsec hiding (spaces)
import Monad
import SourceData
import Data.Tree
import Data.Either
import Control.Monad.Error
--import Language.Haskell.Pretty

symbol :: Parser Char
symbol = oneOf "!#$%&|*+-/:<=>?@^_~"

spaces :: Parser ()
spaces = skipMany space <|> skipMany newline <|> skipMany spaces

load :: String -> IOThrowsError [LispVal]
load filename = (liftIO $ readFile filename) >>= liftThrows . readExprList

readOrThrow :: Parser a -> String -> ThrowsError a
readOrThrow parser input = case parse parser "lisp" input of
    Left err -> throwError $ show err
    Right val -> return val

liftThrows :: ThrowsError a -> IOThrowsError a
liftThrows (Left err) = throwError err
liftThrows (Right val) = return val

readExpr :: String -> ThrowsError LispVal
readExpr = readOrThrow parseExpr
readExprList :: String -> ThrowsError [LispVal]
readExprList = readOrThrow (parseExpr `endBy` spaces)

--fromRight :: Either a b -> b
fromRight (Left a) = error a
fromRight (Right b) = b

type ThrowsError = Either String

type IOThrowsError = ErrorT String IO

--instance Show (IOThrowsError [LispVal]) where
  --  show (ErrorT a) = a


data LispVal = Atom String
             | List [LispVal]
             | DottedList [LispVal] LispVal String
             | Number Integer
             | String String
             | Bool Bool String
             | Comment

instance Show LispVal where
    show (Atom a) = a
    show (List a) = show a
    show (DottedList _ _ a) = a
    show (Number a) = show a
    show (String a) = a
    show (Bool _ a) = a
    show Comment = ""

parseString :: Parser LispVal
parseString = do char '"'
                 x <- many (noneOf "\"")
                 char '"'
                 return $ String x

parseAtom :: Parser LispVal
parseAtom = do first <- letter <|> symbol
               rest <- many (letter <|> digit <|> symbol)
               let atom = first:rest
               return $ case atom of 
                          "#t" -> Bool True atom
                          "#f" -> Bool False atom
                          otherwise -> Atom atom
parseNumber :: Parser LispVal
parseNumber = liftM (Number . read) $ many1 digit

parseComment :: Parser LispVal
parseComment = do
  char ';'
  manyTill anyChar (try newline)
  return Comment

parseList :: Parser LispVal
parseList = do
  s <- parseExpr `sepBy` spaces
  return $ List s

parseDottedList :: Parser LispVal
parseDottedList = do
    head' <- parseExpr `endBy` spaces
    tail' <- char '.' >> spaces >> parseExpr
    return $ DottedList head' tail' ("(" ++ show (head  head') ++ "." ++ show tail' ++ ")")

parseQuoted :: Parser LispVal
parseQuoted = do
    char '\''
    x <- parseExpr
    return $ List [Atom "quote", x]

parseExpr :: Parser LispVal
parseExpr = parseComment
        <|> parseAtom
        <|> parseString
        <|> parseNumber
        <|> parseQuoted
        <|> do char '('
               x <- try parseList <|> try parseDottedList
               char ')'
               return x
-- Gera árvore de tokens para o ydiff
lispValToToken   :: LispVal -> Tree Token
lispValToToken v =  
    let t = Token 1 in
    case v of
      Atom a -> Node (t a "Atom") []
      List a -> Node (t "List" "") (map lispValToToken a)
--      List a -> Node (map lispValToToken a)
      DottedList _ _ s -> Node (t s "Dotted List") []
      Number a -> Node (t (show a) "Number") []
      String a -> Node (t a "String") []
      Bool _ s  -> Node (t s "Bool") []
      Comment -> Node (t "Comment" "Comment") []
        
-- Parseia o código. Função exportada.

--parseScheme str = Node (Token 0 "Program" "Program")  (map lispValToToken (readExpr str))
--parseScheme str = Node (Token 0 "Program" "Program")  (map lispValToToken (readOrThrow str))
--parseScheme = lispValToToken . readExpr

--parseScheme st = map lispValToToken parsed
parseScheme :: String -> Tree Token                 
parseScheme str = Node (Token 0 "Program" "Program") ((map lispValToToken) (fromRight (readExprList str)))

{-
quero ir
ThrowsError [LispVal] -> [LispVal]
Either String [LispVal] -> [LispVal]
ou seja
m a -> a
-}